/*
 * Copyright (C) 2010-2011 Graz University of Technology, Austria. All Rights reserved. 
 *
 * Contact: Simone Kopeinik <simone.kopeinik@tugraz.at>
 * 	   Graz University of Technology, Knowledge Management Institute, Brückenkopfgasse 1/6, 8020 Graz 	
 * 	   <http://www.kmi.tugraz.at/>
 * 
 * This software is part of the TARGET platform developed by the
 * TARGET EC-funded project www.reachyourtarget.org
 *
 * This software is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, version 3.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * The GNU General Public License is available at <http://www.gnu.org/licenses/>.
 *
 */
package at.tugraz.kmi.css.cbkst2.prereq.structurevis;

import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;

import javax.imageio.ImageIO;

import org.apache.commons.collections15.Factory;
import org.apache.commons.collections15.Transformer;

import at.tugraz.kmi.css.cbkst2.CbKSTCompetence;
import at.tugraz.kmi.css.cbkst2.PrerequesiteRelation;
import at.tugraz.kmi.css.cbkst2.prereq.CompetenceManager;
import at.tugraz.kmi.css.cbkst2.prereq.FactoryAdapter;

import com.google.inject.Inject;
import com.google.inject.Provider;
import com.jgoodies.binding.beans.PropertyConnector;
import com.jgoodies.binding.value.ValueModel;

import edu.uci.ics.jung.algorithms.layout.Layout;
import edu.uci.ics.jung.algorithms.layout.StaticLayout;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.graph.SparseGraph;
import edu.uci.ics.jung.visualization.RenderContext;
import edu.uci.ics.jung.visualization.VisualizationViewer;
import edu.uci.ics.jung.visualization.decorators.AbstractVertexShapeTransformer;

/**
 * Hasse Diagram visualisation.
 * 
 * @author Georg Öttl <georg.oettl@gmail.com>
 * 
 */
public final class HasseView extends
		VisualizationViewer<CbKSTCompetence, PrerequesiteRelation> {

	private static final long								serialVersionUID	= 1L;
	private final CompetenceManagerModel					presentationModel;
	private final transient Factory<PrerequesiteRelation>	edgeFactory;
	private final transient Factory<CbKSTCompetence>		vertexFactory;

	@Inject
	HasseView(final Provider<CbKSTCompetence> vertexFactory,
			final Provider<PrerequesiteRelation> edgeFactory,
			CompetenceManagerModel graphModel) {
		// just to get an initial initialization
		super(new StaticLayout<CbKSTCompetence, PrerequesiteRelation>(
				new SparseGraph<CbKSTCompetence, PrerequesiteRelation>()));
		this.presentationModel = graphModel;
		this.vertexFactory = new FactoryAdapter<CbKSTCompetence>(vertexFactory);
		this.edgeFactory = new FactoryAdapter<PrerequesiteRelation>(edgeFactory);
		initComponents();
		initVis();
	}

	@Override
	public void setGraphLayout(
			Layout<CbKSTCompetence, PrerequesiteRelation> layout) {
		super.setGraphLayout(layout);
		initVis();
	}

	/**
	 * Initializes the jung graph visualisation
	 */
	private void initComponents() {
		ValueModel prerequesiteGraph = presentationModel
				.getModel(CompetenceManager.PROPERTY_PREREQUESITE_GRAPH);
		PropertyConnector.connectAndUpdate(new FRLayoutConverter(
				prerequesiteGraph), this, "graphLayout");
	}

	/**
	 * Initializes the jung graph visualisation
	 */
	private void initVis() {
		RenderContext<CbKSTCompetence, PrerequesiteRelation> renderContext2 = getRenderContext();

		setGraphMouse(new HasseGraphMouse(vertexFactory, edgeFactory,
				renderContext2));

		setVertexToolTipTransformer(new Transformer<CbKSTCompetence, String>() {
			@Override
			public String transform(CbKSTCompetence input) {
				return input.getDescription();
			}
		});

		renderContext2
				.setVertexLabelTransformer(new Transformer<CbKSTCompetence, String>() {
					@Override
					public String transform(CbKSTCompetence input) {
						return input.getName();
					}
				});

		VertexShapeSizeAspect<CbKSTCompetence, PrerequesiteRelation> vertexShapeTransformer = new VertexShapeSizeAspect<CbKSTCompetence, PrerequesiteRelation>(
				getModel().getGraphLayout().getGraph());

		renderContext2.setVertexShapeTransformer(vertexShapeTransformer);

	}

	/**
	 * copy the visible part of the graph to a file as a jpeg image
	 * 
	 * @param out
	 *            the outputsteream to save to.
	 * @throws IOException
	 */
	void writeImage(OutputStream out) throws IOException {
		int width = getWidth();
		int height = getHeight();
		BufferedImage bi = new BufferedImage(width, height,
				BufferedImage.TYPE_INT_RGB);
		Graphics2D graphics = bi.createGraphics();
		paint(graphics);
		graphics.dispose();
		ImageIO.write(bi, "png", out);
	}

	private static class VertexShapeSizeAspect<V, E> extends
			AbstractVertexShapeTransformer<V> implements Transformer<V, Shape> {

		private Graph<V, E>			graph;

		private final static int	THRESH	= 10;

		VertexShapeSizeAspect(Graph<V, E> graphIn) {
			this.graph = graphIn;
			setSizeTransformer(new Transformer<V, Integer>() {
				@Override
				public Integer transform(V input) {
					int inDegree = VertexShapeSizeAspect.this.graph
							.inDegree(input);
					int factor = inDegree < THRESH ? 5 : 2;
					return (inDegree + 3) * factor;
				}
			});
		}

		@Override
		public Shape transform(V input) {
			if (!this.graph.containsVertex(input)) {
				this.graph.addVertex(input);
			}

			int inDegree = this.graph.inDegree(input);
			return inDegree < THRESH ? factory.getEllipse(input) : factory
					.getRectangle(input);
		}

	}

};
